using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
#if MOSS
using Microsoft.Office.Server;
using Microsoft.Office.Server.UserProfiles;
#endif
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using PS.WebParts.Rollup.Core;

namespace PS.WebParts.Rollup
{
    public abstract class ARollupDataSourceView : ATabularDataSourceView
    {
        private bool showIsoDates = false;
        private bool fixLookups = true;
        //protected string[] listFieldNames;
        private uint? queryRowLimit;
        private string[] lists;
        private bool includeListData = false;
        private RollUpQueryWebScope webScope = RollUpQueryWebScope.Recursive;
        private bool queryModerator;
        private bool queryRecursive;
        //private bool isWebShared = true;

        Dictionary<string, Regex> matches;
    
        private string query = ""; //string.Empty;

        protected ARollupDataSourceView(HttpContext context, IDataSource owner, string viewName)
            : base(context, owner, viewName)
        {
        }

        [Obsolete, Bindable(false), Browsable(false)]
        public bool EnableQueryStringFilter
        {
            get { return false; }
            set { }
        }

        public bool IncludeListData
        {
            get
            {
                return includeListData;
            }
            set
            {
                includeListData = value;
            }
        }

        public bool QueryModerator
        {
            get
            {
                return queryModerator;
            }
            set
            {
                queryModerator = value;
            }
        }

        public bool QueryRecursive
        {
            get
            {
                return queryRecursive;
            }
            set
            {
                queryRecursive = value;
            }
        }

        private string topLevelWeb;
        public string TopLevelSite
        {
            get
            {
                return topLevelWeb;
            }
            set
            {

                if (value == null)
                    throw new ArgumentNullException("value");
                // VAL> Inserted 1/20/07 to troubleshoot issue with spaces as a part of url
                string val;
                if (value.Contains(" "))
                    val = HttpUtility.UrlEncode(value);
                else
                    val = value;

                if (Uri.IsWellFormedUriString(val, UriKind.Relative) || Uri.IsWellFormedUriString(val, UriKind.Absolute))
                {
                    topLevelWeb = val;
                }
                else
                {
                    //Modifed 1/3/07 By HRamos & Vorekhov
                    //Allow absolute urls to be included for aggregation
                    //try
                    //{
                    //    Uri uri = new Uri(value);
                    //    topLevelWeb = uri.AbsolutePath;
                    //}
                    //catch (Exception)
                    //{
                    LogError("TopLevelSite setting: Only relative URIs can be accepted: " + value);
                    throw new ArgumentException("Only relative URIs can be accepted: " + value);
                    //}
                }
            }
        }
        
        public string GetLists()
        {
            return GetCsvString(lists);
        }

        public void SetLists(string csvLists)
        {
            if (csvLists == null)
                throw new ArgumentNullException();

            Lists = csvLists.Split(',');
        }

        public string[] Lists
        {
            get
            {
                return lists;
            }
            set
            {
                SafeNormalizeStringArray(ref lists, value);
            }
        }


        public RollUpQueryWebScope Scope
        {
            get
            {
                return webScope;
            }
            set
            {
                webScope = value;
            }
        }

        public IDictionary<string, object> Parameters
        {
            get
            { return parameters;
            }
            set
            {
                parameters = value;
            }
        }

        public override DataTable GetSchema()
        {
            DataTable table = new DataTable("Row");
            if (this.IncludeListData)
            {
                table.Columns.Add(new DataColumn("_WebId", typeof(string)));
                table.Columns.Add(new DataColumn("_ListId", typeof(string)));
                table.Columns.Add(new DataColumn("_ListTitle", typeof(string)));
                table.Columns.Add(new DataColumn("_ListUrl", typeof(string)));
                table.Columns.Add(new DataColumn("_SiteTitle", typeof(string)));
                table.Columns.Add(new DataColumn("_SiteUrl", typeof(string)));
                table.Columns.Add(new DataColumn("_ItemId", typeof(string)));
                table.Columns.Add(new DataColumn("_ItemUrl", typeof(string)));
                table.Columns.Add(new DataColumn("_ItemEdit", typeof(string)));
            }

            FieldInfoCollection listFieldNames = this.Fields;

            foreach (FieldInfo fieldInfo in listFieldNames)
            {
                table.Columns.Add(new DataColumn(fieldInfo.GetSchemaName(), typeof(string)));
            }

            return table;
        }

        public override DataTable GetData()
        {
            return GetRollUpData();
        }

        private DataTable GetRollUpData()
        {
            string site = this.TopLevelSite;
            DataTable data;
            if (string.IsNullOrEmpty(site))
            {
                data = GetRollUpData(SPControl.GetContextWeb(base.Context));
            }
            else
                data = GetRollUpData(site);

            return data;
        }

        public DataTable GetRollUpData(string startingUrl)
        {
            using (WebInfo webinfo = GetWeb(startingUrl, base.Context))
            {
                return GetRollUpData(webinfo.Web);
            }
        }
    
        internal static WebInfo GetWeb(string url, HttpContext context)
        {
            SPSite site = new SPSite(ResolveRelativeOrAbsoluteUrl(url, context));
            try
            {
                return new WebInfo(site, site.OpenWeb());
            } catch(Exception)
            {
                site.Dispose();
                throw;
            }
        }

        public DataTable GetRollUpData(SPWeb web)
        {
            if (web == null)
                throw new ArgumentNullException("web");

            if (this.lists == null || this.lists.Length == 0)
                throw new ArgumentException("The Lists property is null or does not contain any lists to roll up");

            FieldInfoCollection fieldInfos = this.Fields;
            if (fieldInfos.Count == 0)
                throw new ArgumentException("The Fields property does not contain any list fields to roll up");

            if (!HasRootWebAccess(web))
            {
                return new DataTable();
            } 

            DataTable data = RollUpData(web);

            if (data == null)
            {
                LogError("Datasource returned a null result set.");
                data = new DataTable();
            }
            DataColumnCollection columns = data.Columns;
            foreach (FieldInfo info in fieldInfos)
            {
                if (!string.IsNullOrEmpty(info.Alias))
                {
                    DataColumn column = columns[info.InternalName];
                    if (column == null)
                    {
                        column = columns[info.InternalName.Replace(" ", "_x0020_")];
                    }
                    if (column!=null)
                    {
                        column.ColumnName = info.Alias;
                    }
                }
            }
            return data;
        }

        protected virtual bool HasRootWebAccess(SPWeb web)
        {
            //Verify that the user has access to the root of the rollup site. return empty table otherwice
            web.Site.CatchAccessDeniedException = false;
            try
            {
                string t = web.Title;
                return true;
            }
            catch (UnauthorizedAccessException)
            {
                return false;
            }
        }

        public abstract DataTable RollUpData(SPWeb web);
        

        protected static void SafeNormalizeStringArray(ref string[] prop, string[] value)
        {
            prop = value;

            if (prop == null)
                prop = new string[0];

            for (int a = 0; a < prop.Length; a++)
            {
                prop[a] = prop[a].Trim();
            }
        }
        
        protected static string GetISODate(Match match)
        {
            string text1 = match.Groups[1].ToString();
            DateTime time1 = DateTime.Now.AddDays(Convert.ToDouble(text1));
            return SPUtility.CreateISO8601DateTimeFromSystemDateTime(time1);
        }

        protected static string ResolveRelativeOrAbsoluteUrl(string url, HttpContext context)
        {
            //Modifed 1/4/07 By HRamos
            //Function name changed to reflect new absolute url functionality
            bool absoluteUri = Uri.IsWellFormedUriString(url, UriKind.Absolute);
            if (absoluteUri)
            {
                return url;
            }
            else
                if (context != null)
                {
                    
                    SPWeb curWeb = SPControl.GetContextWeb(context);
                    string curWebUrl = curWeb.Url;
                    Uri webUri = new Uri(curWebUrl);
                    string serverRelUrl = curWeb.ServerRelativeUrl;
                    string host = webUri.Scheme + "://" + webUri.Authority;
                    string effectiveRelUrl;

                    effectiveRelUrl = Util.ResolveRelUrl(serverRelUrl, url);

                    return host + effectiveRelUrl;
                }
#if TESTS            
                else
                {
                    return "http://localhost" + url;
                }
#else
               throw new ArgumentException("url");
#endif
        }

     
        //internal static SPSite GetSite(string url, HttpContext context)
        //{
        //    return new SPSite(ResolveRelativeOrAbsoluteUrl(url, context));
        //}


        //public string GetListFieldNames()
        //{
        //    return GetCsvString(this.fields);
        //}

        public string GetQueryRowLimit()
        {
            return queryRowLimit != null ? queryRowLimit.ToString() : null;
        }

        public void SetQueryRowLimit(string value)
        {
            if (!string.IsNullOrEmpty(value))
                queryRowLimit = Convert.ToUInt32(value);
            else
                queryRowLimit = null;
        }

        protected static string GetCsvString(string[] prop)
        {
            StringBuilder sb = new StringBuilder();
            if (prop == null)
                return null;

            for (int a = 0; a < prop.Length; a++)
            {
                if (a != 0)
                    sb.Append(',');
                sb.Append(prop[a]);
            }
            return sb.ToString();
        }


        //added by Mr. Eugene to enable awesomeness 1/14/07
        //moved to central CAML replacement

        internal void SetListFieldNames(string csvListFieldNames)
        {
            if (csvListFieldNames == null)
                throw new ArgumentNullException();

            string[] arr = csvListFieldNames.Split(',');
            FieldInfoCollection list = this.Fields;
            list.Clear();
            foreach (string s in arr)
            {
                list.Add(new FieldInfo(s));
            }
        }

        private FieldInfoCollection fields;
        private IDictionary<string, object> parameters;

        public FieldInfoCollection Fields
        {
            get
            {
                if (fields == null)
                    fields = new FieldInfoCollection();
                return fields;
            }
            set
            {
                fields = value;
            }
        }

        //public string[] ListFieldNames
        //{
        //    get
        //    {
        //        return listFieldNames;
        //    }
        //    set
        //    {
        //        SafeNormalizeStringArray(ref listFieldNames, value);
        //    }
        //}

        public string Query
        {
            get
            {
             
                    return query;
            }
            set
            {                
                query = value;
            }
        }

        public uint? QueryRowLimit
        {
            get
            {
                return queryRowLimit;
            }
            set
            {
                queryRowLimit = value;
            }
        }
        
        public bool DatesInISO8601
        {
            get
            {
                return showIsoDates;
            }
            set
            {
                showIsoDates = value;
            }
        }

        public bool FixLookups
        {
            get
            {
                return fixLookups;
            }
            set
            {
                fixLookups = value;
            }
        }


        protected internal string GetMatchValue(string columnName, string regex, string text2)
        {
            if (matches == null) matches = new Dictionary<string, Regex>(fields.Count);

            Regex ex;
            if (matches.ContainsKey(columnName))
            {
                ex = matches[columnName];
            }
            else
            {
                ex = new Regex(regex, RegexOptions.Compiled);
                matches[columnName] = ex;
            }

            Match match = ex.Match(text2);
            if (match.Success)
            {
                return match.Groups[match.Groups.Count - 1].Value;
            } else
            {
                return string.Empty;
            }
        }

        internal protected static string ReplaceQueryLiveFields(string str, SPWeb web, HttpContext context, string cellProvider, IDictionary<string, object> parameters)
        {
            if (context == null)
                context = HttpContext.Current;

            if (str.IndexOf("[QS:") != -1)
            {
                int j = 0;
                while (str.IndexOf("[QS:", j) != -1)
                {
                    string key = str.Substring(str.IndexOf("[QS:") + 4, str.IndexOf("]", j) - (str.IndexOf("[QS:") + 4));
                    string val = context.Request.QueryString[key];
                    if (val != null)
                    {
                        str = str.Replace("[QS:" + key + "]", val);
                    }
                    else
                        break;
                }
            }

            if (parameters != null && parameters.Count > 0 && str.IndexOf("[Parameter:") != -1)
            {
                int j = 0;
                while (str.IndexOf("[Parameter:", j) != -1)
                {
                    string key = str.Substring(str.IndexOf("[Parameter:") + 4, str.IndexOf("]", j) - (str.IndexOf("[Parameter:") + 4));
                    object  val = parameters[key];
                    if (val != null)
                    {
                        str = str.Replace("[Parameter:" + key + "]", val.ToString());
                    }
                    else
                        break;
                }
            }
#if MOSS
            if (str.IndexOf("[Profile:") != -1)
            {
                SPUser user = web.CurrentUser;

                ServerContext serverContext = ServerContext.GetContext(context);
                UserProfileManager profileManager = new UserProfileManager(serverContext);
                string sAccount = user.LoginName;
                UserProfile u = profileManager.GetUserProfile(sAccount);

                int i = 0;
                while (str.IndexOf("[Profile:", i) != -1)
                    try
                    {

                        string key = str.Substring(str.IndexOf("[Profile:") + 9, str.IndexOf("]", i) - (str.IndexOf("[Profile:") + 9));

                        string val = u[key].Value as string;

                        if (val != null)
                            str = str.Replace("[Profile:" + key + "]", val);
                        else 
                            break;
                    }
                    catch (SPException) { }

                //i = str.IndexOf("[Profile:", i + 9);
            }
#endif
            StringBuilder sb = new StringBuilder(str, str.Length + 128);

            if (str.Contains("[Login]") || str.Contains("[UserName]") || str.Contains("[UserID]"))
            {
                SPUser user = web.CurrentUser;
                sb.Replace("[Login]", user.LoginName);
                sb.Replace("[UserName]", user.Name);
                sb.Replace("[UserID]", user.ID.ToString());
            }

            if ( !string.IsNullOrEmpty(cellProvider) )
                sb.Replace("[CellProvider]", cellProvider);

            try
            {
                sb.Replace("[Now]", DateTime.Now.ToString("u"));
                sb.Replace("[SNow]", DateTime.Now.ToShortDateString());
                DateTime time1 = DateTime.Now.AddDays((-DateTime.Now.Day + 1));
                sb.Replace("[FirstMonthDay]", time1.ToString("u"));
                time1 = DateTime.Now.AddDays((-DateTime.Now.Day + 0x1f));
                sb.Replace("[LastMonthDay]", time1.ToString("u"));
                sb.Replace("[SPWeb.ID]", SPContext.Current.Web.ID.ToString());
                sb.Replace("[SPWeb.ID.D]", SPContext.Current.Web.ID.ToString("D"));

                str = Regex.Replace(sb.ToString(), @"\[Now([+-][\d]+)\]", new MatchEvaluator(GetISODate));

                if (str.Contains("[SPWeb.Properties["))
                    str = ReplaceWebProperties(SPContext.Current.Web, str);
            }
            catch (Exception)
            {
            }

            return str;
        }

        internal static string ReplaceWebProperties(SPWeb web, string str)
        {
            Regex r = new Regex(@"\[SPWeb.Properties\[(?<propname>\S+)\]\]");
            Match m = r.Match(str);
            if (m != null)
            {
                string oldString = m.Value;
                string propertyName = m.Groups["propname"].Value;
                if (!string.IsNullOrEmpty(propertyName))
                {
                    try
                    {
                        str = str.Replace(oldString, web.Properties[propertyName]);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
            return str;
        }
    }
}
